package repository

import (
	"JK-Junior-Go-Engineer-Camp/webook/internal/domain"
	"JK-Junior-Go-Engineer-Camp/webook/internal/repository/dao"
	"context"
	"gorm.io/gorm"
)

type ArticleRepository interface {
	Create(ctx context.Context, art domain.Article) (int64, error)
	Update(ctx context.Context, art domain.Article) error
	Sync(ctx context.Context, art domain.Article) (int64, error)
}

type CachedArticleRepository struct {
	dao dao.ArticleDAO

	// SyncV1
	authorDAO dao.ArticleAuthorDAO
	readerDAO dao.ArticleReaderDAO
	// SyncV2
	db *gorm.DB
}

func NewCachedArticleRepository(dao dao.ArticleDAO) ArticleRepository {
	return &CachedArticleRepository{dao: dao}
}

// NewCachedArticleRepositoryV2 Repository层面上分发（非事务实现），V1是service层面上分发
func NewCachedArticleRepositoryV2(authorDao dao.ArticleAuthorDAO,
	readerDao dao.ArticleReaderDAO) *CachedArticleRepository {
	return &CachedArticleRepository{
		authorDAO: authorDao,
		readerDAO: readerDao,
	}
}

func (a *CachedArticleRepository) Create(ctx context.Context, art domain.Article) (int64, error) {
	return a.dao.Insert(ctx, a.domainToEntity(art))
}

func (a *CachedArticleRepository) Update(ctx context.Context, art domain.Article) error {
	return a.dao.UpdateById(ctx, a.domainToEntity(art))
}

// Sync 同步制作库和线上库数据
func (a *CachedArticleRepository) Sync(ctx context.Context, art domain.Article) (int64, error) {
	return a.dao.Sync(ctx, art)
}

// SyncV1 同步制作库和线上库数据-非事务实现
func (a *CachedArticleRepository) SyncV1(ctx context.Context, art domain.Article) (int64, error) {
	artEntity := a.domainToEntity(art)
	var (
		id  = artEntity.Id
		err error
	)
	if id > 0 {
		err = a.authorDAO.Update(ctx, artEntity)
	} else {
		id, err = a.authorDAO.Create(ctx, artEntity)
	}
	if err != nil {
		return 0, err
	}
	artEntity.Id = id // 其实这一句是多余的，因为GORM在上面Create后已经自动赋值了
	err = a.readerDAO.Upsert(ctx, artEntity)
	return id, err
}

// SyncV2 同步制作库和线上库数据-事务实现
func (a *CachedArticleRepository) SyncV2(ctx context.Context, art domain.Article) (int64, error) {
	// 开启事务
	tx := a.db.WithContext(ctx).Begin()
	if tx != nil {
		return 0, tx.Error
	}
	// 防止后续业务panic
	defer tx.Rollback()
	authorDAO := dao.NewArticleAuthorGORMDAO(tx)
	readerDAO := dao.NewArticleReaderGORMDAO(tx)
	artEntity := a.domainToEntity(art)
	var (
		id  = artEntity.Id
		err error
	)
	if id > 0 {
		err = authorDAO.Update(ctx, artEntity)
	} else {
		id, err = authorDAO.Create(ctx, artEntity)
	}
	if err != nil {
		return 0, err
	}
	artEntity.Id = id // 其实这一句是多余的，因为GORM在上面Create后已经自动赋值了
	err = readerDAO.UpsertV2(ctx, dao.PublishedArticle(artEntity))
	if err != nil {
		return 0, err
	}
	// 提交事务
	tx.Commit()
	return id, nil
}

func (a *CachedArticleRepository) domainToEntity(domainArt domain.Article) dao.Article {
	return dao.Article{
		Id:       domainArt.Id,
		Title:    domainArt.Title,
		Content:  domainArt.Content,
		AuthorId: domainArt.Author.Id,
	}
}
